package de.tabiteuea.gui;

import java.io.IOException;

import javax.xml.stream.XMLStreamException;

import org.eclipse.core.runtime.Assert;
import org.eclipse.jface.dialogs.Dialog;
import org.eclipse.jface.dialogs.IInputValidator;
import org.eclipse.jface.dialogs.InputDialog;
import org.eclipse.jface.dialogs.MessageDialog;
import org.eclipse.jface.viewers.ArrayContentProvider;
import org.eclipse.jface.viewers.ISelectionChangedListener;
import org.eclipse.jface.viewers.IStructuredSelection;
import org.eclipse.jface.viewers.SelectionChangedEvent;
import org.eclipse.jface.viewers.TableViewer;
import org.eclipse.jface.viewers.ViewerComparator;
import org.eclipse.jface.window.Window;
import org.eclipse.swt.SWT;
import org.eclipse.swt.dnd.DND;
import org.eclipse.swt.dnd.DropTargetAdapter;
import org.eclipse.swt.dnd.DropTargetEvent;
import org.eclipse.swt.dnd.TextTransfer;
import org.eclipse.swt.dnd.Transfer;
import org.eclipse.swt.events.ModifyEvent;
import org.eclipse.swt.events.ModifyListener;
import org.eclipse.swt.events.SelectionAdapter;
import org.eclipse.swt.events.SelectionEvent;
import org.eclipse.swt.graphics.Color;
import org.eclipse.swt.layout.GridData;
import org.eclipse.swt.layout.GridLayout;
import org.eclipse.swt.widgets.Button;
import org.eclipse.swt.widgets.Combo;
import org.eclipse.swt.widgets.Composite;
import org.eclipse.swt.widgets.Label;
import org.eclipse.swt.widgets.ProgressBar;
import org.eclipse.swt.widgets.Text;
import org.eclipse.ui.ISharedImages;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PartInitException;
import org.eclipse.ui.PlatformUI;
import org.eclipse.ui.part.ViewPart;
import org.eclipse.ui.forms.widgets.FormToolkit;
import org.eclipse.ui.forms.widgets.Section;

import de.tabiteuea.gui.dialogs.OpenFileDialog;
import de.tabiteuea.gui.models.InputModel;
import de.tabiteuea.gui.models.Language;
import de.tabiteuea.gui.utils.ImageConstants;
import de.tabiteuea.gui.utils.ImageProvider;
import de.tabiteuea.gui.utils.TextProcessor;

/**
 * This View contains all elements for the interaction between the program and
 * the user.
 * <p>
 * It consists of a section for the common project management (name, file) and a
 * section for user input (word list, delimiter, language) to calibrate the
 * frequency analysis.
 * 
 * @author Sebastian Lippert
 * 
 */

public class UIView extends ViewPart {

	public static final String ID = "de.tabiteuea.gui.view";//$NON-NLS-1$
	private Section section;
	private Composite mainComp;
	private Label projectName, fileName;
	private InputModel model;
	private Combo textLanguage, baseForm;
	private Button nameButton, pathButton, wordDelIsRegExp, senDelIsRegExp;
	private Button addWord, editWord, removeWord, startButton, clearButton;
	private Text wordDelimiter, sentenceDelimiter;
	private TableViewer wordList;
	private ProgressBar bar;

	/**
	 * Callback to create and initialize the viewer.
	 * 
	 */
	public void createPartControl(Composite parent) {

		parent.getShell().setMaximized(true);
		FormToolkit toolkit = new FormToolkit(parent.getDisplay());

		mainComp = toolkit.createComposite(parent);
		mainComp.setLayout(new GridLayout());
		mainComp.setLayoutData(new GridData(GridData.FILL_BOTH));

		createProjectSection(mainComp, toolkit);
		createAnalysisSection(mainComp, toolkit);
		hookButtonListeners();

		mainComp.setVisible(false);

	}

	/*
	 * Creates the upper part of the view containing the project management
	 */

	private void createProjectSection(Composite parent, FormToolkit toolkit) {

		Color blue = parent.getDisplay().getSystemColor(SWT.COLOR_BLUE);

		// section header
		section = toolkit.createSection(parent, Section.DESCRIPTION
				| Section.TITLE_BAR);
		section.setText(Messages.UlView_projectSec_title);
		section.setDescription(Messages.UlView_projectSec_desc);
		section.setLayout(new GridLayout());
		section.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		section.clientVerticalSpacing = 10;

		Composite comp = toolkit.createComposite(section);
		comp.setLayout(new GridLayout(3, false));
		comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		/*
		 * project name part
		 */
		toolkit.createLabel(comp, Messages.UlView_project_label);
		projectName = toolkit.createLabel(comp, ""); //$NON-NLS-1$
		projectName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		projectName.setForeground(blue);

		nameButton = toolkit.createButton(comp, "", SWT.PUSH); //$NON-NLS-1$
		nameButton.setImage(ImageProvider.getImage(ImageConstants.EDIT));
		nameButton.setToolTipText(Messages.UlView_project_tool);

		/*
		 * project file part
		 */
		toolkit.createLabel(comp, Messages.UlView_file_label);
		fileName = toolkit.createLabel(comp, ""); //$NON-NLS-1$
		fileName.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		fileName.setForeground(blue);

		pathButton = toolkit.createButton(comp, "", SWT.PUSH); //$NON-NLS-1$
		pathButton.setImage(ImageProvider
				.getSharedImage(ISharedImages.IMG_OBJ_FOLDER));
		pathButton.setToolTipText(Messages.UlView_file_tool);

		section.setClient(comp);

	}

	/*
	 * lower part of the view which contains the analyzes management
	 */

	private void createAnalysisSection(Composite parent, FormToolkit toolkit) {

		Label label;
		GridData grid;
		GridLayout layout;

		section = toolkit.createSection(parent, Section.DESCRIPTION
				| Section.TITLE_BAR);
		section.setText(Messages.UlView_ana_title);
		section.setDescription(Messages.UlView_ana_desc);
		section.setLayout(new GridLayout());
		section.setLayoutData(new GridData(GridData.FILL_BOTH));
		section.clientVerticalSpacing = 10;

		Composite comp = toolkit.createComposite(section);
		comp.setLayout(new GridLayout(2, false));
		comp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		/*
		 * language part
		 */

		toolkit.createLabel(comp, Messages.UlView_language);
		textLanguage = new Combo(comp, SWT.READ_ONLY);
		textLanguage.setItems(new String[] { Messages.UlView_lang_english,
				Messages.UlView_lang_german });
		textLanguage.select(0);
		textLanguage.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		toolkit.createLabel(comp, Messages.UlView_baseform);
		baseForm = new Combo(comp, SWT.READ_ONLY);
		baseForm.setToolTipText(Messages.UlView_baseform_tool);
		baseForm.setItems(new String[] { Messages.UlView_false,
				Messages.UlView_true });
		baseForm.select(0);
		baseForm.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		toolkit.createLabel(comp, Messages.UlView_wordDelimit_label);

		layout = new GridLayout(2, false);
		layout.marginHeight = 0;
		layout.marginLeft = -5;
		layout.marginRight = -5;

		/*
		 * delimiter part
		 */

		Composite metaComp = toolkit.createComposite(comp);
		metaComp.setLayout(layout);
		metaComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		wordDelIsRegExp = toolkit.createButton(metaComp,
				Messages.UlView_regExp_button, SWT.CHECK);
		wordDelimiter = toolkit.createText(metaComp, "", SWT.BORDER); //$NON-NLS-1$
		wordDelimiter.setToolTipText(Messages.UlView_wordDelimit_tool);
		wordDelimiter.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		toolkit.createLabel(comp, Messages.UlView_senDelimit_label);

		layout = new GridLayout(2, false);
		layout.marginHeight = 0;
		layout.marginLeft = -5;
		layout.marginRight = -5;

		metaComp = toolkit.createComposite(comp);
		metaComp.setLayout(layout);
		metaComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		senDelIsRegExp = toolkit.createButton(metaComp,
				Messages.UlView_regExp_button, SWT.CHECK);
		sentenceDelimiter = toolkit.createText(metaComp, "", SWT.BORDER); //$NON-NLS-1$
		sentenceDelimiter.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		sentenceDelimiter.setToolTipText(Messages.UlView_senDelimit_tool);

		/*
		 * wordlist part
		 */

		grid = new GridData();
		grid.verticalAlignment = SWT.TOP;
		grid.verticalIndent = 30;

		label = toolkit.createLabel(comp, Messages.UlView_wordList);
		label.setLayoutData(grid);

		layout = new GridLayout(2, false);
		layout.marginHeight = 30;
		layout.marginLeft = -5;
		layout.marginRight = -10;

		metaComp = toolkit.createComposite(comp);
		metaComp.setLayout(layout);
		metaComp.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));

		wordList = new TableViewer(metaComp, SWT.BORDER | SWT.V_SCROLL
				| SWT.SINGLE);
		wordList.getTable().setLayoutData(new GridData(GridData.FILL_BOTH));
		wordList.setContentProvider(new ArrayContentProvider());
		wordList.setComparator(new ViewerComparator());
		wordList.getTable().setToolTipText(Messages.UlView_wordList_tool);

		metaComp = toolkit.createComposite(metaComp);
		metaComp.setLayout(new GridLayout());
		metaComp.setLayoutData(new GridData());

		addWord = toolkit.createButton(metaComp, "", SWT.PUSH); //$NON-NLS-1$
		addWord.setToolTipText(Messages.UlView_addWord_tool);
		addWord.setImage(ImageProvider.getImage(ImageConstants.NEW));

		editWord = toolkit.createButton(metaComp, "", SWT.PUSH); //$NON-NLS-1$
		editWord.setToolTipText(Messages.UlView_editWord_tool);
		editWord.setEnabled(false);
		editWord.setImage(ImageProvider.getImage(ImageConstants.EDIT));

		removeWord = toolkit.createButton(metaComp, "", SWT.PUSH); //$NON-NLS-1$
		removeWord.setToolTipText(Messages.UlView_removeWord_tool);
		removeWord.setImage(ImageProvider.getImage(ImageConstants.REMOVE));
		removeWord.setEnabled(false);

		clearButton = toolkit.createButton(metaComp, "", SWT.PUSH);
		clearButton.setToolTipText(Messages.UlView_clearWord_tool);
		clearButton.setImage(ImageProvider.getImage(ImageConstants.CLEAR));

		/*
		 * start and progress bar
		 */

		startButton = toolkit.createButton(comp, "Start", SWT.PUSH);
		startButton.setImage(ImageProvider.getImage(ImageConstants.START));
		startButton.setToolTipText(Messages.UlView_start_tooltip);

		bar = new ProgressBar(comp, SWT.SMOOTH);
		bar.setLayoutData(new GridData(GridData.FILL_HORIZONTAL));
		bar.setVisible(false);

		section.setClient(comp);

	}

	/**
	 * Passing the focus request to the viewer's control.
	 */

	public void setFocus() {
		// not in use
	}

	/**
	 * Load a model into the User Interface and refresh it. That means all
	 * attributes from the model will be set as values for fields,
	 * table viewers and so on.
	 * 
	 * @param model
	 *            The new model
	 */

	public void loadModel(InputModel model) {

		this.model = model;

		projectName.setText(model.getProjectName());
		fileName.setText(model.getFileName());
		wordDelimiter.setText(model.getWordDelimiter());
		sentenceDelimiter.setText(model.getSentenceDelimiter());
		wordDelIsRegExp.setSelection(model.isWordDelIsRegExp());
		senDelIsRegExp.setSelection(model.isSenDelIsRegExp());
		wordList.setInput(model.getWordList());

		switch (model.getTextLanguage()) {

		case en:
			textLanguage.select(0);
			break;
		case de:
			textLanguage.select(1);
			break;

		}

		if (model.isUseBaseform())
			baseForm.select(1);
		else
			baseForm.select(0);

		mainComp.setVisible(true);

	}

	/*
	 * Adds listeners to all buttons, fields and tableViewers to handle them.
	 * Handle them means that the model will be updated after every input. Every
	 * entry represents more or less one model attribute.
	 */

	private void hookButtonListeners() {

		/*
		 * Handles the project name attribute of the model
		 */

		nameButton.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {

				// call a common input dialog and set title and description
				InputDialog dia = new InputDialog(mainComp.getShell(),
						Messages.UlView_newProjectName,
						Messages.UlView_newProjectName_desc, projectName
								.getText(), new IInputValidator() {

							@Override
							public String isValid(String newText) {

								// check if empty
								if (newText.trim().isEmpty())
									return Messages.UlView_newProjectName_error_empty;

								return null;
							}
						});

				// OK was pressed so update the model
				if (dia.open() == Window.OK) {
					Assert.isNotNull(model);
					projectName.setText(dia.getValue());
					model.setProjectName(dia.getValue());
				}
			}
		});

		/*
		 * Handles the file path attribute of the model
		 */

		pathButton.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {

				try {

					// call file dialog with according parameters
					OpenFileDialog fileDialog = new OpenFileDialog(mainComp,
							Messages.OpenFileDialog_open, new String[] {
									"*.csv;*.txt;*.xml", "*.csv", "*.txt",
									"*.xml" });

					// OK was pressed so update the model
					// includes file path, file name and the corpus
					if (!fileDialog.isInterrupted()) {
						Assert.isNotNull(model);
						fileName.setText(fileDialog.getFileName());
						model.setFileName(fileDialog.getFileName());
						model.setFilePath(fileDialog.getPath());
						model.setCorpus(fileDialog.getCorpus());
					}
				} catch (IOException e1) {
					MessageDialog.openError(mainComp.getShell(),
							Messages.UlView_newProjectName_error,
							Messages.UlView_newProjectFile_error_couldNotOpen
									+ " " + e1.getLocalizedMessage());
				} catch (XMLStreamException e2) {
					MessageDialog.openError(mainComp.getShell(),
							Messages.UlView_newProjectName_error,
							Messages.UlView_newProjectFile_error_couldNotOpen
									+ " " + e2.getLocalizedMessage());
					e2.printStackTrace();
				}
			}

		});

		/*
		 * Handles the language attribute of the model
		 */

		textLanguage.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				Assert.isNotNull(model);
				int selection = textLanguage.getSelectionIndex();

				switch (selection) {
				case 0:
					// set english as language of the text
					model.setTextLanguage(Language.en);
					break;
				case 1:
					// set german as language of the text
					model.setTextLanguage(Language.de);
					break;
				}

			}

		});

		/*
		 * Handles the baseForm attribute of the model
		 */

		baseForm.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				Assert.isNotNull(model);
				int selection = baseForm.getSelectionIndex();

				switch (selection) {
				case 0:
					model.setUseBaseform(false);
					break;
				case 1:
					model.setUseBaseform(true);
					break;
				}

			}

		});

		/*
		 * Handles the wordDelIsRegExp attribute of the model
		 */

		wordDelIsRegExp.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				Assert.isNotNull(model);
				model.setWordDelIsRegExp(wordDelIsRegExp.getSelection());

			}
		});

		/*
		 * Handles the senDelIsRegExp attribute of the model
		 */

		senDelIsRegExp.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				Assert.isNotNull(model);
				model.setSenDelIsRegExp(senDelIsRegExp.getSelection());

			}
		});

		/*
		 * Handles the wordDelimiter attribute of the model
		 */

		wordDelimiter.addModifyListener(new ModifyListener() {

			@Override
			public void modifyText(ModifyEvent e) {
				Assert.isNotNull(model);
				model.setWordDelimiter(wordDelimiter.getText());

			}
		});

		/*
		 * Handles the sentenceDelimiter attribute of the model
		 */

		sentenceDelimiter.addModifyListener(new ModifyListener() {

			@Override
			public void modifyText(ModifyEvent e) {
				Assert.isNotNull(model);
				model.setSentenceDelimiter(sentenceDelimiter.getText());

			}
		});

		/*
		 * Handles the activation of the remove an edit button for the word
		 * list. A empty list or no selected entry disables the according
		 * buttons.
		 */

		wordList.addSelectionChangedListener(new ISelectionChangedListener() {

			@Override
			public void selectionChanged(SelectionChangedEvent event) {

				IStructuredSelection sel = (IStructuredSelection) event
						.getSelection();
				boolean select = sel.getFirstElement() != null;
				editWord.setEnabled(select);
				removeWord.setEnabled(select);

			}
		});

		/*
		 * Adds drag and drop support. Allows to add items from the frequency
		 * table to the word coverage list.
		 */

		wordList.addDropSupport(DND.DROP_MOVE,
				new Transfer[] { TextTransfer.getInstance() },
				new DropTargetAdapter() {

					public void drop(DropTargetEvent event) {

						/*
						 * no further control of the input is needed cause there
						 * is only one drag and drop source, which is the
						 * frequency table
						 */
						String word = ((String) event.data).toLowerCase();
						Assert.isNotNull(model);

						// word list is treated as set
						if (!model.wordListContains(word)) {
							model.addWordToWordList(word);
							wordList.add(word);
						}

					}
				});

		/*
		 * Adds a word to the list/model if the word isn't already in the 
		 * list/model
		 */

		addWord.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				InputDialog dia = new InputDialog(mainComp.getShell(),
						Messages.UlView_newWordDia_head,
						Messages.UlView_newWordDia_desc,
						"", new IInputValidator() { //$NON-NLS-2$

							public String isValid(String newText) {

								Assert.isNotNull(model);

								// unique test
								if (model.wordListContains(newText))
									return Messages.UlView_wordDia_alreadyExisting;

								// empty test
								if (newText.equals("")) //$NON-NLS-1$
									return ""; //$NON-NLS-1$

								return null;
							}

						});

				if (Dialog.OK == dia.open()) {
					Assert.isNotNull(model);
					model.addWordToWordList(dia.getValue().toLowerCase());
					wordList.add(dia.getValue().toLowerCase());
				}
			}
		});

		/*
		 * Edit the selected list entry and re-add it to the list/model if the
		 * word isn't already in the list/model
		 */

		editWord.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {

				IStructuredSelection sel = (IStructuredSelection) wordList
						.getSelection();

				InputDialog dia = new InputDialog(mainComp.getShell(),
						Messages.UlView_editWordDia_head,
						Messages.UlView_editWordDia_desc, sel.getFirstElement()
								.toString(), new IInputValidator() {

							public String isValid(String newText) {

								Assert.isNotNull(model);

								// unique test
								if (model.wordListContains(newText))
									return Messages.UlView_wordDia_alreadyExisting;

								// empty test
								if (newText.equals("")) //$NON-NLS-1$
									return ""; //$NON-NLS-1$

								return null;
							}

						});

				if (Dialog.OK == dia.open()) {
					Assert.isNotNull(model);
					wordList.remove(sel.getFirstElement());
					wordList.add(dia.getValue().toLowerCase());
					model.removeWordFromWordList(sel.getFirstElement()
							.toString());
					model.addWordToWordList(dia.getValue().toLowerCase());
				}
			}
		});

		/*
		 * Removes selected entry form the list/model
		 */

		removeWord.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				Assert.isNotNull(model);
				IStructuredSelection sel = (IStructuredSelection) wordList
						.getSelection();
				wordList.remove(sel.getFirstElement());
				model.removeWordFromWordList(sel.getFirstElement().toString());
			}
		});

		/*
		 * Clear the word list
		 */

		clearButton.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {
				Assert.isNotNull(model);

				wordList.setInput(null);
				model.clearWordList();

			}
		});

		/*
		 * This is where the magic happens and coffee will be cooked
		 */

		startButton.addSelectionListener(new SelectionAdapter() {

			@Override
			public void widgetSelected(SelectionEvent e) {

				// long start = System.currentTimeMillis();

				// create TextProcessor that parses the input
				Assert.isNotNull(model);
				TextProcessor processor = new TextProcessor(model
						.getTextLanguage(), model.isUseBaseform());

				/*
				 * if return value is false, the delimiters couldn't be compiled
				 * (thats the common reason)
				 * if return value is true, all calculations are done and the 
				 * results are ready to present
				 */
				if (processor.parseText(bar, model.getCorpus(),
						model.getWordDelimiter(), model.getSentenceDelimiter(),
						model.isWordDelIsRegExp(), model.isSenDelIsRegExp(),
						model.getWordList())) {

					IWorkbenchWindow activeWorkbenchWindow = PlatformUI
							.getWorkbench().getActiveWorkbenchWindow();
					IWorkbenchPage activePage = activeWorkbenchWindow
							.getActivePage();

					try {

						// try to activate or show the Result-View
						ResultView view = (ResultView) activePage
								.findView(ResultView.ID);
						if (view == null)
							view = (ResultView) activePage
									.showView(ResultView.ID);
						else
							activePage.activate(view);

						// add frequency and statistic list to result view
						view.setInput(processor.getCountArray(),
								processor.getStatisticArray());

						// System.out.println(System.currentTimeMillis() -
						// start);

					} catch (PartInitException e1) {
						throw new RuntimeException(e1);
					}

				}
			}
		});

	}
}
